import { BaseService } from '@/core/base.service';
import { AddressQueryDto } from '@/dtos';
import { Injectable } from '@nestjs/common';
import { Address, Prisma, User } from '@prisma/client';

@Injectable()
export class AddressService extends BaseService {
  async query(ownerId: number, { params, sort }: AddressQueryDto) {
    const { current = 1, pageSize = 20, keyword, ...where } = params;

    let sql = `
      SELECT #{fields} FROM Address AS t
      WHERE t.isDeleted = false
        AND t.ownerId = ${ownerId}
    `;

    const [{ count }] = await this.prisma.$queryRawUnsafe<[{ count: number }]>(
      sql.replace('#{fields}', 'COUNT(t.id) AS count'),
    );

    const list = await this.prisma.$queryRawUnsafe<Address[]>(
      `${sql.replace(
        '#{fields}',
        `t.id, t.mobile, t.name, t.isDefault, t.address, t.area`,
      )}
      ORDER BY ${
        !!sort && Object.keys(sort).length > 0
          ? Object.entries(sort)
              .map(([k, v]) => `t.${k} ${v === 'descend' ? 'DESC' : 'ASC'}`)
              .join(',')
          : 't.createdAt DESC'
      }
      LIMIT ${pageSize} 
      OFFSET ${(current - 1) * pageSize}
      `,
    );

    const total = count ? Number(count) : 0;
    const totalPage = Math.ceil(total / pageSize);
    const hasMore = current < totalPage;

    return {
      total,
      totalPage,
      hasMore,
      current,
      pageSize,
      list,
    };
  }

  async create(
    ownerId: number,
    data: Prisma.AddressCreateWithoutOwnerInput,
  ): Promise<Address> {
    if (data.isDefault) {
      await this.prisma.address.updateMany({
        where: {
          ownerId,
        },
        data: {
          isDefault: false,
        },
      });
    }
    return await this.prisma.address.create({
      data: {
        ...data,
        ownerId,
      },
    });
  }

  async update(params: {
    where: Prisma.AddressWhereUniqueInput;
    data: Prisma.AddressUpdateInput;
  }): Promise<Address> {
    const { where, data } = params;

    if (data.isDefault) {
      await this.prisma.address.updateMany({
        where: {
          id: { not: where.id },
          ownerId: where.ownerId,
        },
        data: {
          isDefault: false,
        },
      });
    } else {
      const address = await this.prisma.address.findUnique({
        where: {
          id: where.id,
          ownerId: where.ownerId,
        },
      });
      if (address.isDefault) {
        data.isDefault = true;
      }
    }
    return this.prisma.address.update({
      where,
      data: {
        ...data,
      },
    });
  }

  async remove(where: Prisma.AddressWhereUniqueInput): Promise<Address> {
    const data = await this.prisma.address.findUnique({
      where,
    });
    if (data.isDefault) {
      await this.prisma.$executeRawUnsafe(`
        UPDATE Address AS t
        SET t.isDefault = true
        WHERE t.isDeleted = false
          AND t.isDefault = false
          AND t.ownerId = ${data.ownerId}
        ORDER BY t.createdAt DESC
        LIMIT 1
      `);
    }
    return await this.prisma.address.update({
      where,
      data: {
        isDeleted: true,
      },
    });
  }
}
